#!/usr/bin/env python
# -*- coding: utf-8 -*-

__author__ = 'James Iter'
__date__ = '15/12/27'
__contact__ = 'james.iter.cn@gmail.com'
__copyright__ = '(c) 2015 by James Iter.'


import traceback
import jimit as ji
import mysql.connector
from mysql.connector import errorcode
import json
from random import choice
import sys

sys.path.append("..")
from models import Utils
from models.initialize import app, Init, logger, objects_model, object_db_map, db_conn_map, cold_db_conn_map, states, \
    cold_db_conn_map_list, cold_db_conn_map_list_reversed


cycle_date = int(Utils.ts_to_date(ji.Common.ts()) / 100)


def sql_stmt_fetchall(_cnx=None, _sql_stmt=None):
    if _cnx is None or _sql_stmt is None:
        raise ValueError('_cnx and _sql_stmt must not None')

    _cursor = _cnx.cursor(dictionary=True, buffered=False)
    try:
        _cursor.execute(_sql_stmt)
        return _cursor.fetchall()
    except mysql.connector.Error as err:
        ret = dict()
        ret['state'] = ji.Common.exchange_state(50050)
        ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', err._full_msg])
        logger.error(_sql_stmt)
        logger.error(err)
        raise ji.PreviewingError(json.dumps(ret))
    finally:
        _cursor.close()
        _cnx.close()


def sql_stmt_delete(_cnx=None, _sql_stmt=None):
    if _cnx is None or _sql_stmt is None:
        raise ValueError('_cnx and _sql_stmt must not None')

    _cursor = _cnx.cursor(dictionary=True, buffered=False)
    try:
        _cursor.execute(_sql_stmt)
        _cnx.commit()
    except mysql.connector.Error as err:
        ret = dict()
        ret['state'] = ji.Common.exchange_state(50050)
        ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', err._full_msg])
        logger.error(_sql_stmt)
        logger.error(err)
        raise ji.PreviewingError(json.dumps(ret))
    finally:
        _cursor.close()
        _cnx.close()


def record_rows(path=None, rows=None):
    with open(path, 'a') as f:
        for row in rows:
            f.write(json.dumps(row))
            f.write('\n')


if __name__ == '__main__':
    # noinspection PyBroadException
    try:
        Init.init_db_conn()
        Init.init_cold_db_conn()
        Init.init_object_db_map()
        Init.init_objects_model()

        # 热库可写组
        hot_db_s = db_conn_map[states.DBDomain.hot.value]['db_group_s'][0]
        # 当期的冷库
        cold_db_s = None

        for cold_object_db in cold_db_conn_map_list:
            if cold_object_db['sequence'] == cycle_date:
                cold_db_s = cold_object_db['cnx_s']
                break

        if cold_db_s is None:
            logger.critical('cold_db_s must not None')
            print 'cold_db_s must not None'
            exit()

        # 获取适当链接
        cold_cnxpool = choice(cold_db_s)

        # 遍历热库中的名字
        for name, object_db in object_db_map.items():
            if object_db['domain'] != states.DBDomain.hot.value:
                continue

            # 尝试从热库取存在的最小id值
            min_sql_stmt = ''.join(['SELECT id FROM ', name, ' ORDER BY id LIMIT 1'])
            # 尝试从冷库取当期最大id值
            max_sql_stmt = ''.join(['SELECT id FROM ', name, ' ORDER BY id DESC LIMIT 1'])

            min_rows = []
            if object_db['rw_mode'] == states.RWMode.BW_AR.value:
                # 聚合读
                for cnxpool in hot_db_s:
                    cnx = cnxpool.get_connection()
                    min_rows.extend(sql_stmt_fetchall(_cnx=cnx, _sql_stmt=min_sql_stmt))

            elif object_db['rw_mode'] == states.RWMode.SW_SR.value:
                # 随机取一个来读
                cnxpool = choice(hot_db_s)
                cnx = cnxpool.get_connection()
                min_rows.extend(sql_stmt_fetchall(_cnx=cnx, _sql_stmt=min_sql_stmt))
            else:
                raise OSError('What?')

            cnx = cold_cnxpool.get_connection()
            # max_rows从冷库取,只有一条,所以不存在排序问题
            max_rows = sql_stmt_fetchall(_cnx=cnx, _sql_stmt=max_sql_stmt)

            # min_rows从多个热库取得,可能有多条,所以需要排序
            min_rows.sort()

            if min_rows.__len__() < 1 or max_rows.__len__() < 1:
                print '当期无转存数据'
                exit()

            # 取对象在热库中的最小id
            min_id = min_rows[0]['id']
            # 取对象在冷库中的最大id
            max_id = max_rows[0]['id']

            print ''.join(['MIN_ID: ', str(min_id)])
            print ''.join(['MAX_ID: ', str(max_id)])
            # 处理清除的逻辑
            step = 100
            clear_min_id = min_id
            clear_max_id = 0
            # 最后一次清除,dump_max_id必定等于max_id
            while clear_max_id < max_id:
                clear_max_id = clear_min_id + step
                if clear_max_id > max_id:
                    clear_max_id = max_id

                # 将被清除的数据获取语句 默认以主键id顺序返回,这样可以在后面的排序中相对减少复杂度
                select_sql_stmt = ''.join(['SELECT * FROM ', name, ' WHERE ', str(clear_min_id),
                                          ' <= id AND id <= ', str(clear_max_id)])
                delete_sql_stmt = ''.join(['DELETE FROM ', name, ' WHERE ', str(clear_min_id),
                                           ' <= id AND id <= ', str(clear_max_id)])
                clear_min_id = clear_max_id
                clear_rows = list()
                if object_db['rw_mode'] == states.RWMode.BW_AR.value:
                    # 取最后一组来聚合读
                    for cnxpool in hot_db_s:
                        cnx = cnxpool.get_connection()
                        clear_rows.extend(sql_stmt_fetchall(_cnx=cnx, _sql_stmt=select_sql_stmt))
                        cnx = cnxpool.get_connection()
                        sql_stmt_delete(_cnx=cnx, _sql_stmt=delete_sql_stmt)

                elif object_db['rw_mode'] == states.RWMode.SW_SR.value:
                    # 随机取最后一组中的一个来读
                    cnxpool = choice(hot_db_s)
                    cnx = cnxpool.get_connection()
                    clear_rows = sql_stmt_fetchall(_cnx=cnx, _sql_stmt=select_sql_stmt)
                    sql_stmt_delete(_cnx=cnx, _sql_stmt=delete_sql_stmt)
                else:
                    raise OSError('What?')

                if object_db['rw_mode'] == states.RWMode.BW_AR.value:
                    clear_rows.sort(key=lambda _item: _item['id'])

                record_rows(path=''.join(['./', str(cycle_date), '_', name, '.record']), rows=clear_rows)

    except:
        logger.error(traceback.format_exc())
